/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef HMFS_RESIZE_UTILS_H
#define HMFS_RESIZE_UTILS_H

#include <string>
#include <sys/types.h>

#include "hmfs_common.h"
#include "hmfs_io.h"
#include "hmfs_zoned.h"
#include "resize_data.h"

namespace OHOS {
namespace Hmfs {

inline bool IsCheckpointFlags(struct CheckPointData *cpData, unsigned int flag)
{
    unsigned int ckptFlags = LE32_TO_NATIVE(cpData->cpFlags);
    return ckptFlags & flag ? 1 : 0;
}

inline bool NeedFsyncDataRecord(std::shared_ptr<HmfsSbInfo> sbInfo_, std::shared_ptr<HmfsConfigData> config_)
{
    return !IsCheckpointFlags(sbInfo_->ckptData, CP_UMOUNT_FLAG) ||
        config_->zonedModel == HMFS_ZONED_HM;
}

inline uint32_t EndBlockAddr(std::shared_ptr<HmfsSbInfo> sbInfo_)
{
    return SM_I(sbInfo_)->mainBlkaddr +
        (LE32_TO_NATIVE(F2FS_RAW_SUPER(sbInfo_)->segmentCountInMain) <<
        sbInfo_->logBlksPerSeg);
}

inline int ExistQfIno(struct SuperBlockData *superBlock)
{
    int i;
    for (i = 0; i < HMFS_MAX_QUOTAS; i++) {
        if (superBlock->qfInodeId[i]) {
            return 1;
        }
    }
    return 0;
}

inline void CheckSegRange(std::shared_ptr<HmfsSbInfo> sbInfo_, unsigned int segno)
{
    unsigned int endSegno = sbInfo_->smInfoTable->segmentCount - 1;
    ASSERT(segno <= endSegno);
}

inline uint32_t CurrentSitAddr(std::shared_ptr<HmfsSbInfo> sbInfo_,unsigned int segno)
{
    SegmentInfoTable * smInfo = sbInfo_->smInfoTable;
    struct SitInfo *sitInfo = smInfo->sitInfo;
    unsigned int offset = SIT_BLOCK_OFFSET(sitInfo, segno);
    uint32_t blkAddr = sitInfo->sitBaseAddr + offset;

    CheckSegRange(sbInfo_, segno);

    /* calculate sit block address */
    if (HmfsCommon::GetInstance().HmfsTestBit(offset, sitInfo->sitBitmap)) {
        blkAddr += sitInfo->sitBlocks;
    }
    return blkAddr;
}

inline void GetCurrentSitPage(std::shared_ptr<HmfsSbInfo> sbInfo_, unsigned int segno, struct sitBlockData *sitBlk)
{
    uint32_t blkAddr = CurrentSitAddr(sbInfo_, segno);
    ASSERT(HmfsIo::GetInstance().DevReadBlock(sitBlk, blkAddr) >= 0);
}

inline struct SegEntry* GetSegEntry(std::shared_ptr<HmfsSbInfo> sbInfo_, unsigned int segno)
{
    struct SitInfo *sitInfo = sbInfo_->smInfoTable->sitInfo;
    return &sitInfo->segEntries[segno];
}

inline void ResetCurSegment(std::shared_ptr<HmfsSbInfo> sbInfo_, int32_t type)
{
    struct CurSegmentInfo *curseg = static_cast<struct CurSegmentInfo *>(sbInfo_->smInfoTable->curSegmentArray + type);

    struct SummaryFooter *sumFooter = &(curseg->segSumBlk->footer);
    memset(sumFooter, 0, sizeof(struct SummaryFooter));
    if (IS_DATASEG(type)) {
        SET_SUM_TYPE(sumFooter, SUM_TYPE_DATA);
    }
    if (IS_NODESEG(type)) {
        SET_SUM_TYPE(sumFooter, SUM_TYPE_NODE);
    }
    struct SegEntry *se = GetSegEntry(sbInfo_, curseg->segNum);
    se->type = type;
    se->dirty = 1;
}

inline void PrintSbDataFeature(struct SuperBlockData *sbData)
{
    uint32_t f = sbData->features;
    HMFS_DEBUG("superblock features = %x : ", f);
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_ENCRYPT)) {
        HMFS_DEBUG("%s", " encrypt");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_VERITY)) {
        HMFS_DEBUG("%s", " verity");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_BLKZONED)) {
        HMFS_DEBUG("%s", " blkzoned");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_EXTRA_ATTR)) {
        HMFS_DEBUG("%s", " extra_attr");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_PRJQUOTA)) {
        HMFS_DEBUG("%s", " project_quota");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_INODE_CHKSUM)) {
        HMFS_DEBUG("%s", " inode_checksum");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_FLEXIBLE_INLINE_XATTR)) {
        HMFS_DEBUG("%s", " flexible_inline_xattr");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_QUOTA_INO)) {
        HMFS_DEBUG("%s", " quota_ino");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_INODE_CRTIME)) {
        HMFS_DEBUG("%s", " inode_crtime");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_LOST_FOUND)) {
        HMFS_DEBUG("%s", " lost_found");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_SB_CHKSUM)) {
        HMFS_DEBUG("%s", " sb_checksum");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_CASEFOLD)) {
        HMFS_DEBUG("%s", " casefold");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_COMPRESSION)) {
        HMFS_DEBUG("%s", " compression");
    }
    if (f & NATIVE_TO_LE32(HMFS_FEATURE_RO)) {
        HMFS_DEBUG("%s", " ro");
    }
    HMFS_DEBUG("\n");
    HMFS_DEBUG("superblock encrypt level = %d, salt = ", sbData->encryptionLevel);
    for (int i = 0; i < 16; i++) {
        HMFS_DEBUG("%02x", sbData->encryptPwSalt[i]);
    }
}

inline void PrintSbDataStopReason(struct SuperBlockData *sbData, int forceStop)
{
    if(!forceStop) {
        return;
    }
    uint8_t *reason = sbData->stopReason;
    HMFS_DEBUG("checkpoint stop reason: ");
    std::string stopReasonStr[] = {
        [STOP_CP_REASON_SHUTDOWN]        = "shutdown",
        [STOP_CP_REASON_FAULT_INJECT]        = "fault_inject",
        [STOP_CP_REASON_META_PAGE]        = "meta_page",
        [STOP_CP_REASON_WRITE_FAIL]        = "write_fail",
        [STOP_CP_REASON_CORRUPTED_SUMMARY]    = "corrupted_summary",
        [STOP_CP_REASON_UPDATE_INODE]        = "update_inode",
        [STOP_CP_REASON_FLUSH_FAIL]        = "flush_fail",
    };
    for (int i = 0; i < STOP_CP_REASON_MAX; i++) {
        if (reason[i])
            HMFS_DEBUG("%s(%d) ", stopReasonStr[i].c_str(), reason[i]);
    }
}

inline int TestBitLe(uint32_t nr, const uint8_t *addr)
{
    return ((1 << (nr & 7)) & (addr[nr >> 3]));
}

enum f2fs_error {
    ERROR_CORRUPTED_CLUSTER,
    ERROR_FAIL_DECOMPRESSION,
    ERROR_INVALID_BLKADDR,
    ERROR_CORRUPTED_DIRENT,
    ERROR_CORRUPTED_INODE,
    ERROR_INCONSISTENT_SUMMARY,
    ERROR_INCONSISTENT_FOOTER,
    ERROR_INCONSISTENT_SUM_TYPE,
    ERROR_CORRUPTED_JOURNAL,
    ERROR_INCONSISTENT_NODE_COUNT,
    ERROR_INCONSISTENT_BLOCK_COUNT,
    ERROR_INVALID_CURSEG,
    ERROR_INCONSISTENT_SIT,
    ERROR_CORRUPTED_VERITY_XATTR,
    ERROR_CORRUPTED_XATTR,
    ERROR_MAX,
};

inline void PrintSbDataErrors(struct SuperBlockData *sbData, int32_t hmfsErrors)
{
    if (!hmfsErrors) {
        return;
    }
    std::string errorsStr[] = {
        [ERROR_CORRUPTED_CLUSTER]        = "corrupted_cluster",
        [ERROR_FAIL_DECOMPRESSION]        = "fail_decompression",
        [ERROR_INVALID_BLKADDR]            = "invalid_blkaddr",
        [ERROR_CORRUPTED_DIRENT]        = "corrupted_dirent",
        [ERROR_CORRUPTED_INODE]            = "corrupted_inode",
        [ERROR_INCONSISTENT_SUMMARY]        = "inconsistent_summary",
        [ERROR_INCONSISTENT_FOOTER]        = "inconsistent_footer",
        [ERROR_INCONSISTENT_SUM_TYPE]        = "inconsistent_sum_type",
        [ERROR_CORRUPTED_JOURNAL]        = "corrupted_journal",
        [ERROR_INCONSISTENT_NODE_COUNT]        = "inconsistent_node_count",
        [ERROR_INCONSISTENT_BLOCK_COUNT]    = "inconsistent_block_count",
        [ERROR_INVALID_CURSEG]            = "invalid_curseg",
        [ERROR_INCONSISTENT_SIT]        = "inconsistent_sit",
        [ERROR_CORRUPTED_VERITY_XATTR]        = "corrupted_verity_xattr",
        [ERROR_CORRUPTED_XATTR]            = "corrupted_xattr",
    };
    uint8_t *errors = sbData->errors;
    HMFS_DEBUG("hmfs errors: ");
    for (int i = 0; i < ERROR_MAX; i++) {
        if (TestBitLe(i, errors))
            HMFS_DEBUG("%s ",  errorsStr[i].c_str());
    }
}

inline void PrintRawSbInfo(struct SuperBlockData *sb, int debugLevel, int32_t layout)
{
    if (!debugLevel) {
        return;
    }
    if(layout) {
        printf("\n");
        printf("+--------------------------------------------------------+\n");
        printf("| Super block                                            |\n");
        printf("+--------------------------------------------------------+\n");
    }
    PRINT_TO_COLSOLE(sb, magicNo);
    PRINT_TO_COLSOLE(sb, majorVersion);
    // DISP_label(sb->volumeName);
    PRINT_TO_COLSOLE(sb, minorVersion);
    PRINT_TO_COLSOLE(sb, logSectorSize);
    PRINT_TO_COLSOLE(sb, logSectorsPerBlk);

    PRINT_TO_COLSOLE(sb, logBlockSize);
    PRINT_TO_COLSOLE(sb, logBlksPerSeg);
    PRINT_TO_COLSOLE(sb, segsPerSection);
    PRINT_TO_COLSOLE(sb, sectionsPerZone);
    PRINT_TO_COLSOLE(sb, checksumOffset);
    PRINT_TO_COLSOLE(sb, blockCount);

    PRINT_TO_COLSOLE(sb, sectionCount);
    PRINT_TO_COLSOLE(sb, segmentCount);
    PRINT_TO_COLSOLE(sb, segmentCountInCP);
    PRINT_TO_COLSOLE(sb, segmentCountInSIT);
    PRINT_TO_COLSOLE(sb, segmentCountInNAT);

    PRINT_TO_COLSOLE(sb, segmentCountInSSA);
    PRINT_TO_COLSOLE(sb, segmentCountInMain);
    PRINT_TO_COLSOLE(sb, segment0BlkId);

    PRINT_TO_COLSOLE(sb, cpBlkId);
    PRINT_TO_COLSOLE(sb, sitBlkId);
    PRINT_TO_COLSOLE(sb, natBlkId);
    PRINT_TO_COLSOLE(sb, ssaBlkId);
    PRINT_TO_COLSOLE(sb, mainBlkId);

    PRINT_TO_COLSOLE(sb, rootInodeId);
    PRINT_TO_COLSOLE(sb, nodeInodeId);
    PRINT_TO_COLSOLE(sb, metaInodeId);
    PRINT_TO_COLSOLE(sb, cpPayload);
    // PRINT_TO_COLSOLE(sb, crc);
    printf("%-25s%-.255s", "version", sb->version);
    printf("\n");
}

inline void PrintCheckpointInfo(struct CheckPointData *cp, int debugLevel, int32_t layout)
{
    if (!debugLevel) {
        return;
    }
    if(layout) {
        printf("\n");
        printf("+--------------------------------------------------------+\n");
        printf("| Checkpoint                                             |\n");
        printf("+--------------------------------------------------------+\n");
    }
    PRINT_TO_COLSOLE(cp, cpVersion);
    PRINT_TO_COLSOLE(cp, userBlockCount);
    PRINT_TO_COLSOLE(cp, validBlockCount);
    PRINT_TO_COLSOLE(cp, rsvdSegmentCount);
    PRINT_TO_COLSOLE(cp, overprovSegmentCount);
    PRINT_TO_COLSOLE(cp, freeSegmentCount);

    PRINT_TO_COLSOLE(cp, allocType[CURSEG_HOT_NODE]);
    PRINT_TO_COLSOLE(cp, allocType[CURSEG_WARM_NODE]);
    PRINT_TO_COLSOLE(cp, allocType[CURSEG_COLD_NODE]);
    PRINT_TO_COLSOLE(cp, curNodeSegNo[0]);
    PRINT_TO_COLSOLE(cp, curNodeSegNo[1]);
    PRINT_TO_COLSOLE(cp, curNodeSegNo[2]);

    PRINT_TO_COLSOLE(cp, curNodeBlkOffset[0]);
    PRINT_TO_COLSOLE(cp, curNodeBlkOffset[1]);
    PRINT_TO_COLSOLE(cp, curNodeBlkOffset[2]);


    PRINT_TO_COLSOLE(cp, allocType[CURSEG_HOT_DATA]);
    PRINT_TO_COLSOLE(cp, allocType[CURSEG_WARM_DATA]);
    PRINT_TO_COLSOLE(cp, allocType[CURSEG_COLD_DATA]);
    PRINT_TO_COLSOLE(cp, curDataSegNo[0]);
    PRINT_TO_COLSOLE(cp, curDataSegNo[1]);
    PRINT_TO_COLSOLE(cp, curDataSegNo[2]);

    PRINT_TO_COLSOLE(cp, curDataBlkOffset[0]);
    PRINT_TO_COLSOLE(cp, curDataBlkOffset[1]);
    PRINT_TO_COLSOLE(cp, curDataBlkOffset[2]);

    PRINT_TO_COLSOLE(cp, cpFlags);
    PRINT_TO_COLSOLE(cp, cpPackBlockCount);
    PRINT_TO_COLSOLE(cp, cpPackStartSum);
    PRINT_TO_COLSOLE(cp, validNodeCount);
    PRINT_TO_COLSOLE(cp, validInodeCount);
    PRINT_TO_COLSOLE(cp, nextFreeNodeId);
    PRINT_TO_COLSOLE(cp, sitVersionBitmapSize);
    PRINT_TO_COLSOLE(cp, natVersionBitmapSize);
    PRINT_TO_COLSOLE(cp, checksumOffset);
    PRINT_TO_COLSOLE(cp, elapsedTime);

    PRINT_TO_COLSOLE(cp, sitNatVersionBitmap[0]);
    printf("\n\n");
}

} // namespace Hmfs
} // namespace OHOS

#endif // HMFS_RESIZE_UTILS_H